﻿using System;
using System.Collections.Generic;
using System.Text;

namespace MathTools.Methods
{
    public class Eiler2 : AbstractCalculator
    {
        #region Fields

        volatile IList<double> x;
        volatile IList<double> y;
        volatile IList<double> z;
        volatile IList<double> t;

        #endregion

        public Eiler2()
            :base()
        { }

        private double LastIterAbs()
        {
            int length = x.Count;

            return
                Math.Abs( x[ length - 1 ] - x[ length - 2 ] ) +
                Math.Abs( y[ length - 1 ] - y[ length - 2 ] ) +
                Math.Abs( z[ length - 1 ] - z[ length - 2 ] ); 
        }

        private bool checkCircle( double eps )
        {
            int length = x.Count;
            int count = ( int )Math.Log( length );

            for( int i = 2; i < count; i++ )
            {
                if( Math.Abs( x[ length - 1 ] - x[ length - i ] ) +
                    Math.Abs( y[ length - 1 ] - y[ length - i ] ) +
                    Math.Abs( z[ length - 1 ] - z[ length - i ] ) < eps )
                    return true;
            }

            return false;
        }

        protected override double[ , ] solve( double eps )
        {
            double dt = Step;

            x = new List<double>();
            y = new List<double>();
            z = new List<double>();
            t = new List<double>();

            x.Add( X0 );
            y.Add( Y0 );
            z.Add( Z0 );
            t.Add( 0 );

            do
            {
                int lenght = x.Count;
                double prevX = x[ lenght - 1 ];
                double prevY = y[ lenght - 1 ];
                double prevZ = z[ lenght - 1 ];
                double prevT = t[ lenght - 1 ];

                double newX = FX( prevX, prevY, prevZ );
                double newY = FY( prevX, prevY, prevZ );
                double newZ = FZ( prevX, prevY, prevZ );

                double prgX = prevX + newX * dt;
                double prgY = prevX + newY * dt;
                double prgZ = prevX + newZ * dt;

                x.Add( prevX + ( FX( prgX, prgY, prgZ ) + newX ) * dt / 2 );
                y.Add( prevY + ( FY( prgX, prgY, prgZ ) + newY ) * dt / 2 );
                z.Add( prevZ + ( FZ( prgX, prgY, prgZ ) + newZ ) * dt / 2 );
                t.Add( prevT + dt );

            } while( LastIterAbs() > eps && !checkCircle( eps ) && !MustStop && t.Count < MaxCount );

            int steps = x.Count;
            double[ , ] result = new double[ 4, steps ];

            for( int i = 0; i < steps; i++ )
            {
                result[ 0, i ] = t[ i ];
                result[ 1, i ] = x[ i ];
                result[ 2, i ] = y[ i ];
                result[ 3, i ] = z[ i ];

            }
            return result;
        }
        
        public override int CurrentStep
        {
            get
            {
                if( t == null )
                    return 0;

                return t.Count;
            }
        }

    }
}
